package com.eurobcoin.controller;

import java.text.MessageFormat;
import java.util.List;
import java.util.Locale;

import javax.validation.Valid;

import org.apache.ibatis.session.SqlSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;

import com.eurobcoin.ApplicationConst;
import com.eurobcoin.dao.mapper.FilipidesEmailMapper;
import com.eurobcoin.dao.model.FilipidesEmail;
import com.eurobcoin.dao.model.FilipidesEmailExample;
import com.eurobcoin.dao.model.FilipidesEmailExample.Criteria;
import com.eurobcoin.filipides.model.EmailForm;
import com.eurobcoin.helper.PasswdHelper;
import com.eurobcoin.helper.TextsHelper;
import com.eurobcoin.manager.DAOManager;
import com.eurobcoin.manager.StaticTextsManager;
import com.eurobcoin.worker.MailWorker;

@Controller
@RequestMapping("/filipides")
public class FilipidesController {

	private static final Logger logger = LoggerFactory.getLogger(FilipidesController.class);

	private static final String INDEX_VIEW = "filipides/index";
	private static final String SAVE_EMAIL_VIEW = "filipides/save_email";
	private static final String CONFIRM_EMAIL_VIEW = "filipides/confirm";

	private static final String INDEX_REDIRECT = "redirect:/filipides/index.html";

	@Autowired
	private DAOManager daoManager;
	
	@Autowired
	private StaticTextsManager staticTextsManager;
	
	@Autowired
	private MailWorker mailWorker;
	
	private ReloadableResourceBundleMessageSource messageSource;

	@ModelAttribute("emailForm")
	public EmailForm createEmailForm() {
		return new EmailForm();
	}
	
	@Autowired
	public void setMessageSource(ReloadableResourceBundleMessageSource messageSource) {
		this.messageSource = messageSource;
	}

	@RequestMapping(value="/index.html", method=RequestMethod.GET)
	public ModelAndView doIndex(@ModelAttribute(value="emailForm") EmailForm emailForm) throws Exception {

		ModelAndView ret = new ModelAndView(INDEX_VIEW);

		return ret;
	}

	@RequestMapping(value="/save.html", method=RequestMethod.POST)
	public ModelAndView doSaveEmail(
			@Valid @ModelAttribute(value="emailForm") EmailForm emailForm, 
			BindingResult result) throws Exception {

		Locale locale = LocaleContextHolder.getLocale(); 
		
		ModelAndView ret = null;

		if(result.hasErrors()) {
			
			ret = new ModelAndView(INDEX_VIEW);
			
		} else {
			
			SqlSession sqlSession = this.daoManager.openSession();

			boolean emailUnique = this.isEmailUnique(sqlSession, emailForm.getEmail().toLowerCase());

			if(emailUnique) {

				String hash = this.generateHash(sqlSession);

				this.storeEmail(sqlSession, emailForm.getEmail().toLowerCase(), hash);
				this.queueEmail(sqlSession, emailForm.getEmail().toLowerCase(), hash, locale);

				ret = new ModelAndView(SAVE_EMAIL_VIEW);
				ret.addObject("email", emailForm.getEmail());
				
				// reset emailForm
				emailForm.reset();
				
				sqlSession.commit();
				
			} else {

				emailForm.setError(true);
				emailForm.setErrorMsg(messageSource.getMessage(
						"validation.error.email.duplicated", null, locale));
				
				ret = new ModelAndView(INDEX_REDIRECT);

			}
			
			sqlSession.close();  
			
		}

		return ret;
	}

	@RequestMapping(value="/confirm.html", method=RequestMethod.GET)
	public ModelAndView confirmEmail(
			@RequestParam("hash") String hash) throws Exception {
		
		SqlSession sqlSession = this.daoManager.openSession();
		
		ModelAndView ret = new ModelAndView(CONFIRM_EMAIL_VIEW);
		
		String email = this.processHash(sqlSession, hash);
		
		if(email == null) {
			ret.addObject("confirmed", false);
		} else {
			ret.addObject("confirmed", true);
			ret.addObject("email", email);
			
			sqlSession.commit();
		}
		
		sqlSession.close();
		
		return ret;
		
	}
	
	private boolean isEmailUnique(SqlSession sqlSession, String email) {

		FilipidesEmailMapper mapper = sqlSession.getMapper(FilipidesEmailMapper.class);
		FilipidesEmailExample example = new FilipidesEmailExample();
		Criteria criteria = example.createCriteria();
		criteria.andFil_emailEqualTo(email);

		int count = mapper.countByExample(example);

		return count == 0; 

	}

	private String generateHash(SqlSession sqlSession) {

		String ret = null;
		
		boolean unique = false;
		while(!unique) {

			ret = PasswdHelper.generateHash();

			FilipidesEmailMapper mapper = sqlSession.getMapper(FilipidesEmailMapper.class);
			FilipidesEmailExample example = new FilipidesEmailExample();
			Criteria criteria = example.createCriteria();
			criteria.andFil_hashEqualTo(ret.toString());

			int count = mapper.countByExample(example);

			if(count == 0)
				unique = true;

		}

		return ret;
	}

	private void storeEmail(SqlSession sqlSession, String email, String hash) {
		FilipidesEmail filipidesEmail = new FilipidesEmail();
		filipidesEmail.setFil_confirmed(ApplicationConst.NO_VALUE);
		filipidesEmail.setFil_email(email);
		filipidesEmail.setFil_hash(hash); 

		FilipidesEmailMapper mapper = sqlSession.getMapper(FilipidesEmailMapper.class);

		mapper.insert(filipidesEmail);
	}

	private void queueEmail(SqlSession sqlSession, String email, String hash, Locale locale) {
		String urlTemplate = TextsHelper.getStaticText(
				sqlSession, 
				TextsHelper.TXT_LBL_REG_USR_CONFIRM_URL, 
				locale.getLanguage());
		String emailBodyTemplate = TextsHelper.getStaticText(
				sqlSession, 
				TextsHelper.TXT_LBL_REG_USR_EMAIL_BODY, 
				locale.getLanguage());

		String emailSubject = TextsHelper.getStaticText(
				sqlSession, 
				TextsHelper.TXT_LBL_REG_USR_EMAIL_SUBJECT, 
				locale.getLanguage());

		String url = MessageFormat.format(urlTemplate, hash);
		String emailBody = MessageFormat.format(emailBodyTemplate, url);
		
		this.mailWorker.queueEmail(email, email, emailSubject, emailBody, sqlSession);
	}
	
	private String processHash(SqlSession session, String hash) {
		
		String ret = null;
		
		FilipidesEmailMapper mapper = session.getMapper(FilipidesEmailMapper.class);
		FilipidesEmailExample example = new FilipidesEmailExample();
		Criteria criteria = example.createCriteria();
		criteria.andFil_hashEqualTo(hash);
		criteria.andFil_confirmedEqualTo(ApplicationConst.NO_VALUE);
		
		List<FilipidesEmail> listFilipidesEmail = mapper.selectByExample(example);
		FilipidesEmail filipidesEmail = null;
		
		if(listFilipidesEmail.size() == 1) {
			filipidesEmail = listFilipidesEmail.get(0);
			
			filipidesEmail.setFil_confirmed(ApplicationConst.YES_VALUE);
			filipidesEmail.setFil_hash(null);
			
			mapper.updateByPrimaryKey(filipidesEmail);
			
			ret = filipidesEmail.getFil_email();
		}
		
		return ret;
	}

}